﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;

using Daybreak.Core.SceneGraph;
using Daybreak.Core.Terrains;
using Daybreak.Core.Rendering.Pipeline;

namespace Daybreak.Core.Rendering
{
    /// <summary>
    /// Walks through the scene and builds RenderQueue
    /// </summary>
    public class RenderVisitor : NodeVisitor, IRender
    {
        RenderQueue _queue;
        Camera _camera;
        Pipes.FrameContext _frameData = new Pipes.FrameContext();

        RenderArgs curItem;

        float totalTime;
        int frameCounter;

        bool _debugMode = true;

        public RenderVisitor()
        {
            _queue = new RenderQueue();
        }

        public RenderVisitor(RenderQueue queue)
        {
            _queue = queue;
        }

        public void RenderScene(Scene scene, Camera camera, float timeSinceLastFrame)
        {
            curItem.TransformMatrix = Matrix.Identity;
            curItem.TransformNode = null;
            _camera = camera;

            _queue.ResetQueue(false);

            scene.SceneInstance.ApplyVisitor(this);

            frameCounter++;
            totalTime += timeSinceLastFrame;

        }

        private void SetupFrameAttributes()
        {

        }

        public override void VisitGroup(Group group)
        {
            for (int i = 0; i < group.ChildrenCount; i++)
            {
                // save current transform before going deeper through hierarchy
                Matrix saveMatrix = curItem.TransformMatrix;
                Transform saveNode = curItem.TransformNode;

                // go deeper through hierarchy ...
                group[i].Accept(this);

                curItem.TransformMatrix = saveMatrix;
                curItem.TransformNode = saveNode;
            }

        }

        public override void VisitTransform(Transform transform)
        {
            transform.LocalToWorld(ref curItem.TransformMatrix);

            curItem.TransformNode = transform;

            // save world transform
            Matrix saveMatrix = curItem.TransformMatrix;
            Transform saveNode = curItem.TransformNode;

            // go deeper
            VisitGroup(transform);

            // restore transform
            curItem.TransformMatrix = saveMatrix;
            curItem.TransformNode = saveNode;
        }

        public override void VisitGeode(Geode geode)
        {
            
            for (int i = 0; i < geode.Parts.Count; i++)
            {
                Drawable.Container geomContainer = geode.Parts[i];

                if (geomContainer == null)
                    continue;

                curItem.BoundingBox = new BoundingBox();
                geomContainer.MergeBounding(ref curItem.BoundingBox);

                // transform bounding volume to world space
                Transform.TransformBox(ref curItem.BoundingBox, ref curItem.TransformMatrix, out curItem.BoundingBox);

                ContainmentType ctype = ContainmentType.Contains;

                _camera.Frustum.Contains(ref curItem.BoundingBox, out ctype);

                if (ctype == ContainmentType.Disjoint)
                    continue;

                geomContainer.RenderSelf(this, ref curItem);

            }
        }

        public override void VisitTerrain(TerrainNode terrain)
        {
            terrain.RenderSelf(this, ref curItem);
        }

        #region IRender Members

        public RenderQueue RenderQueue
        {
            get { return _queue; }
        }

        public Pipes.FrameContext FrameData
        {
            get { return _frameData; }
        }


        public bool Debug
        {
            get { return _debugMode; }
            set { _debugMode = value; }
        }

        #endregion
    }

    /// <summary>
    /// Base interface for classes that can manage RenderQueue
    /// </summary>
    public interface IRender
    {
        bool Debug { get; set; }
        RenderQueue RenderQueue { get; }
        Pipes.FrameContext FrameData { get; }
    }

}
